/*!
 * \file Mem.h
 * \brief Memory module.
 * \author Nus
 * \date 2008/02/17, 21:37
 */

#ifndef __MEM_H__
#define __MEM_H__

#include "Types.h"
#include "Mutex.h"

//! \brief Safe delete.
#define SAFE_DELETE(_obj) if(_obj) { delete _obj; _obj = NULL; }
//! \brief Safe delete.
#define SAFE_DELETE_ARRAY(_obj) if(_obj) { delete[] _obj; _obj = NULL; }

//! \brief Memory handle.
//! \warning Not thread safe yet.
struct sMemoryHandle {
  //! \brief Memory header.
  //! \todo Decrease memory header size.
  struct sMemHeader {
    sMemoryHandle* handle;        //!< Handle.
    sMemHeader* next;             //!< Next header.
    sMemHeader* prev;             //!< Previous header.
    void* alloc_addr;             //!< Real address.
    void* addr;                   //!< Address.
    const s8* file_loc;           //!< File location.
    s32 line_loc;                 //!< Line location.
    u32 magic;                    //!< Magic number.
    u32 tag;                      //!< Tag number.
    size_t allocated;             //!< Allocated size.
    size_t mem_size;              //!< Memory size.
  };
  
  sMemHeader* mem_chain;          //!< Memory chain.
  sMemHeader* last;               //!< Last entry.
  Mutex mutex;                    //!< Mutex.
  u64 used_memory;                //!< Used memory size.
  u32 auto_tag;                   //!< Tag value.

};

extern sMemoryHandle SysMemHandle;
extern sMemoryHandle* CurrentMemHandle;

extern "C"
{

//! \brief Basic memory allocator.
void* memAllocFromHandle(sMemoryHandle* p_mh,
                         size_t size,
                         s32 align = 32,
                         const s8* file_loc = NULL,
                         s32 line = 0);

//! \brief Basic memory deallocator.
void memFree(void* addr);

//! \brief Basic system memory allocator.
void* memAlloc(size_t size, s32 align = 32,
                        const s8* file_loc = NULL,
                        s32 line = 0);

//! \brief Calculate auto tag.
u32 CalcAutoTag(sMemoryHandle* p_mh);

//! \brief Dump memory info.
void DumpInfo(const sMemoryHandle* p_mh = NULL);

//! \brief Dump memory info (detailed version).
void DumpInfoEx(const sMemoryHandle* p_mh = NULL);

//! \brief Get current memory handle.
sMemoryHandle* GetCurrentMemoryHandle(void);

//! \brief Set current memory handle.
//! \return Previous memory handle.
sMemoryHandle* SetCurrentMemoryHandle(sMemoryHandle* handle);

}

//! \brief Overriding default new operator.
inline void* operator new (size_t size) __attribute__ ((always_inline));
inline void* operator new (size_t size)
{
  if(!CurrentMemHandle)
    CurrentMemHandle = &SysMemHandle;
  return memAllocFromHandle(CurrentMemHandle, size);
}

//! \brief Overriding default delete operator.
inline void operator delete (void* addr) __attribute__ ((always_inline));
inline void operator delete (void* addr)
{
  memFree(addr);
}

//! \brief Overriding default new[] operator.
inline void* operator new[] (size_t size) __attribute__ ((always_inline));
inline void* operator new[] (size_t size)
{
  if(!CurrentMemHandle)
    CurrentMemHandle = &SysMemHandle;
  return memAllocFromHandle(CurrentMemHandle, size);
}

//! \brief Overriding default delete[] operator.
inline void operator delete[] (void* addr) __attribute__ ((always_inline));
inline void operator delete[] (void* addr)
{
  memFree(addr);
}

//! \brief New operator for debugging.
inline void* operator new (size_t size, u32 align, const s8* file_loc, s32 line_loc)
{
  if(!CurrentMemHandle)
    CurrentMemHandle = &SysMemHandle;
  return memAllocFromHandle(CurrentMemHandle, size, align, file_loc, line_loc);
}

//! \brief New operator for debugging.
inline void* operator new[] (size_t size, u32 align, const s8* file_loc, s32 line_loc)
{
  if(!CurrentMemHandle)
    CurrentMemHandle = &SysMemHandle;
  return memAllocFromHandle(CurrentMemHandle, size, align, file_loc, line_loc);
}

#if defined(_DEBUG)

//! \brief Defining new macro.
#define nuNew new (32, __FILE__, __LINE__)
//! \brief Defining new with alignment.
#define nuNewAlign(_a) new (_a, __FILE__, __LINE__)
//! \brief Defining delete macro.
#define nuDelete delete

#elif !defined(_FINAL)

//! \brief Defining new macro.
#define nuNew new (32, NULL, NULL)
//! \brief Defining new with alignment.
#define nuNewAlign(_a) new (_a, NULL, NULL)
//! \brief Defining delete macro.
#define nuDelete delete

#endif

#endif
